package com.aim.analysis.records;

import com.aim.analysis.records.bean.AlertData;
import com.aim.game.MC;
import com.aim.util.ListUtil;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * User: Avraham Shvartzon
 * Date: 6/23/14
 */
public class GameProcessor{

    public final Game game;

    public GameProcessor(Game game) {
        this.game = game;
    }

    public void init(){
        maintenanceProcess();
        repairProcess();
        refuelProcess();
        alertProcess();
        performanceProcess();

        validateScores();
    }

    private void maintenanceProcess() {
        int numOfMaintenance = game.numOfRecords(RecordType.maintenaneAction);

        game.setMaintenanceTime(numOfMaintenance * Scenario.instance().maintenanceTime);
        game.setMaintenanceCost(numOfMaintenance * Scenario.instance().maintenanceCost);
    }

    private void repairProcess() {
        List<GameRecord> records = game.getRecords(RecordType.repairAction);
        for (GameRecord record : records) {
            game.repairCounter.increase(record.mechanicalCondition);
        }

        game.repairCost += game.repairCounter.get(MC.M1) * Scenario.instance().minorCost;
        game.repairCost += game.repairCounter.get(MC.M2) * Scenario.instance().moderateCost;
        game.repairCost += game.repairCounter.get(MC.M3) * Scenario.instance().severeCost;

        game.repairTime += game.repairCounter.get(MC.M1) * Scenario.instance().minorTime;
        game.repairTime += game.repairCounter.get(MC.M2) * Scenario.instance().moderateTime;
        game.repairTime += game.repairCounter.get(MC.M3) * Scenario.instance().severeTime;
    }

    private void refuelProcess() {
        List<GameRecord> refuelRecords = game.getRecords(RecordType.fuelFill);
        for (GameRecord refuelRecord : refuelRecords) {
            game.stationRefuelCost += (Scenario.instance().maxFuelInventory - refuelRecord.fuelLevel)/1000;
        }
        game.spaceshipRefuelCost += game.numOfRecords(RecordType.fuelFillBySpaceship) * Scenario.instance().REFUEL_BY_SPACESHIP_COST;
        game.totalRefuelCost = game.stationRefuelCost + game.spaceshipRefuelCost;
        game.refuelTime += game.numOfRecords(RecordType.fuelFillBySpaceship) * Scenario.instance().REFUEL_BY_SPACESHIP_TIME;
    }

    private void performanceProcess() {
        game.freezeTime = game.maintenanceTime + game.repairTime+ game.refuelTime;

        int scores = game.totalShutdown * Scenario.instance().shutDownScore
                -  game.totalCollision * Scenario.instance().collisionCost
                -  game.stationRefuelCost;
        game.netTime = game.getGameLength() - game.freezeTime;
        game.netScore = 1d * scores / (game.netTime/1000);

        PerformanceHolder p = new PerformanceHolder(game);
        p.process();
        game.avgMSE = p.avgMSE();
        game.segmentMSE = p.segmentMSE();
    }

    private void alertProcess() {
        proccessOldAlertResponseData();
        proccessAlertData();
    }

    private void proccessAlertData() {
        List<GameRecord> records = game.getRecords(RecordType.alertBeforeDisplayed);
        if (records.isEmpty()){
            return;
        }

        List<Integer> recommendedTimes = new ArrayList<Integer>();
        List<Integer> displayTimes = new ArrayList<Integer>();
        List<Integer> responseDelays = new ArrayList<Integer>();
        List<Double> netScoreSegments = new ArrayList<Double>();

        for (GameRecord record : records) {
            AlertData alertData = record.getAlertData();
            if (alertData != null){
               recommendedTimes.add(alertData.recomendedTime);
               displayTimes.add(alertData.displayTime);
               int responseDelay = alertData.recomendedTime - alertData.displayTime;
               assert responseDelay >= 0;
               responseDelays.add(responseDelay);
               netScoreSegments.add(alertData.netScore);
            }
        }
        game.recommendedTimeForAlert = ListUtil.calcStatData(recommendedTimes);
        game.responseDelay = ListUtil.calcStatData(responseDelays);
        game.numOfAlerts = records.size();

        //

    }

    private void proccessOldAlertResponseData() {
        List<Integer> alrMntIntervals = new ArrayList<Integer>();
        List<Integer> alrMalfuncIntervals = new ArrayList<Integer>();

        Set<GameRecord> records = game.getRecords();
        GameRecord lastAlertRecord = null;
        for (GameRecord record : records) {
            if (record.recordType != RecordType.alertBeforeDisplayed && lastAlertRecord == null){
                continue;
            }

            switch (record.recordType){
                case alertBeforeDisplayed: lastAlertRecord = record; break;
                case maintenaneAction:
                    assert (record.time - lastAlertRecord.time) >= 0;
                    alrMntIntervals.add(record.time - lastAlertRecord.time);
                    lastAlertRecord = null;
                    break;
                case mechanicalConditionChanged:
                    assert (record.time - lastAlertRecord.time) > 0;
                    alrMalfuncIntervals.add(record.time - lastAlertRecord.time);
                    lastAlertRecord = null;
                    break;
            }
        }

        game.responseDelayData = new ArrayList<Integer>(alrMntIntervals);
        game.maintenanceAfterAlertDelay = ListUtil.meanAndVariance(alrMntIntervals);
        game.numOfMaintenanceAfterAlert = alrMntIntervals.size();
        game.malfunctionAfterAlertDelay = ListUtil.meanAndVariance(alrMalfuncIntervals);
        game.numOfMalfunctionAfterAlert = alrMalfuncIntervals.size();
    }

    private void validateScores() {
        game.calculatedScore = game.totalShutdown * Scenario.instance().shutDownScore
                - (game.totalCollision * Scenario.instance().collisionCost)
                - (game.maintenanceCost + game.repairCost + game.stationRefuelCost + game.spaceshipRefuelCost);
        //assert game.getFinalScore() == scores : "expected: " + game.getFinalScore() + ", calculated: " + scores;
    }
}
